/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 bitmap_layer.h

	$Header: /resource_editor/bitmap_layer.h $

	$NoKeywords: $

 ************************************************************************/
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "bitmap_layer.h"

#include "alpha_blend.h"
#include "hue_pixel.h"
#include "progress_handler.h"
#include "streambuf_operators.h"

enum t_block_alpha
{
	k_block_transparent,
	k_block_opaque,
	k_block_one_bit_alpha,
	k_block_four_bit_alpha,
};

int const k_block_shift	= 5;
int const k_block_size	= 1 << k_block_shift;
int const k_block_mask  = k_block_size - 1;

// --------------------------------------------------------
// function objects for drawing
// --------------------------------------------------------
namespace
{
// --------------------------------------------------------
// pixel source objects
// 16 bit source
// --------------------------------------------------------
	class t_16_bit_source
	{
	public:
		typedef t_uint16 t_source_type;

		t_uint16 get_pixel( t_uint16 dest, t_uint16 source )
		{
			return source;
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint16 source, int alpha )
		{
			return alpha_blend16( dest, source, alpha );
		}
	};

// --------------------------------------------------------
// pixel source objects
// 16 bit source with alpha blending
// --------------------------------------------------------
	class t_blended_16_bit_source : public t_16_bit_source
	{
	protected:
		int m_alpha;
	public:
		t_blended_16_bit_source( int alpha ) : m_alpha( alpha )
		{
		}
		 
		t_uint16 get_pixel( t_uint16 dest, t_uint16 source )
		{
			return alpha_blend16( dest, source, m_alpha );
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint16 source, int alpha )
		{
			return alpha_blend16( dest, source, multiply_alpha16( m_alpha, alpha ));
		}
	};

// --------------------------------------------------------
// pixel source objects
// 16 bit paletted source
// --------------------------------------------------------
	class t_paletted_source
	{
	protected:
		t_uint16 const* m_palette;
	public:
		typedef t_uint8 t_source_type;

		t_paletted_source( t_uint16 const* palette ) : m_palette( palette )
		{
		}

		t_uint16 get_pixel( t_uint16 dest, t_uint8 index )
		{
			return m_palette[index];
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint8 index, int alpha )
		{
			return alpha_blend16( dest, m_palette[ index ], alpha );
		}
	};

// --------------------------------------------------------
// pixel source objects
// 16 bit paletted source with alpha blending
// --------------------------------------------------------
	class t_blended_paletted_source : public t_paletted_source
	{
	protected:
		int m_alpha;
	public:
		t_blended_paletted_source( t_uint16 const* palette, int alpha ) 
			: t_paletted_source( palette ), m_alpha( alpha )
		{
		}
		
		t_uint16 get_pixel( t_uint16 dest, t_uint8 index )
		{
			return alpha_blend16( dest, m_palette[index], m_alpha );
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint8 index, int alpha )
		{
			return alpha_blend16( dest, m_palette[index], multiply_alpha16( m_alpha, alpha ));
		}
	};

// --------------------------------------------------------
// pixel source objects
// 24 bit paletted source
// --------------------------------------------------------
	class t_paletted_24_source
	{
	protected:
		t_pixel_24 const* m_palette;
	public:
		typedef t_uint8 t_source_type;

		t_paletted_24_source( t_pixel_24 const* palette ) : m_palette( palette )
		{
		}

		t_uint16 get_pixel( t_uint16 dest, t_uint8 index )
		{
			return convert_to_16_bit( m_palette[index] );
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint8 index, int alpha )
		{
			return alpha_blend16( dest, convert_to_16_bit( m_palette[ index ] ), alpha );
		}
	};

// --------------------------------------------------------
// pixel source objects
// 16 bit paletted source with alpha blending
// --------------------------------------------------------
	class t_blended_paletted_24_source : public t_paletted_24_source
	{
	protected:
		int m_alpha;
	public:
		t_blended_paletted_24_source( t_pixel_24 const* palette, int alpha ) 
			: t_paletted_24_source( palette ), m_alpha( alpha )
		{
		}
		
		t_uint16 get_pixel( t_uint16 dest, t_uint8 index )
		{
			return alpha_blend16( dest, convert_to_16_bit( m_palette[index] ), m_alpha );
		}
		t_uint16 blend_pixel( t_uint16 dest, t_uint8 index, int alpha )
		{
			return alpha_blend16( dest, convert_to_16_bit( m_palette[index] ),
				                  multiply_alpha16( m_alpha, alpha ));
		}
	};


// --------------------------------------------------------
// draw objects
// draw using no alpha
// --------------------------------------------------------

	template <class t_filter >
	class t_simple_draw
	{
	protected:
		t_filter m_filter;
	public:
		typedef typename t_filter::t_source_type t_source_type;

		t_simple_draw( t_filter filter ) : m_filter( filter )
		{
		}

		void draw( int count, t_uint16* dest_ptr, t_source_type const* source, 
			       int offset )
		{
			while (count--)
			{
				*dest_ptr = m_filter.get_pixel( *dest_ptr, *source++ );
				dest_ptr++;
			}
		}
	};

// --------------------------------------------------------
// draw objects
// draw using 1 bit alpha
// --------------------------------------------------------
	template <class t_filter >
	class t_draw_alpha_1
	{
	protected:
		t_filter m_filter;
	public:
		typedef typename t_filter::t_source_type t_source_type;

		t_draw_alpha_1( t_filter filter ) : m_filter( filter )
		{
		}

		void draw( int count, t_uint16* dest_ptr, t_source_type const* source, 
			       int offset )
		{
            while (count--)
            {
                if (*source != 0)
                    *dest_ptr = m_filter.get_pixel( *dest_ptr, *source );
                dest_ptr++;
                source++;
            }
		}
	};

// --------------------------------------------------------
// draw objects
// draw using 4 bit alpha
// --------------------------------------------------------
	template <class t_filter>
	class t_draw_alpha_4
	{
	protected:
		t_filter       m_filter;
		t_uint8 const* m_alpha_mask;
	public:
		typedef typename t_filter::t_source_type t_source_type;

		t_draw_alpha_4( t_filter filter, t_uint8 const* alpha_mask ) : m_filter( filter ),
			            m_alpha_mask( alpha_mask )
		{
		}

		void draw( int count, t_uint16* dest_ptr, t_source_type const* source, 
			       int offset )
        {   // find start
			t_uint8 const* alpha_source = m_alpha_mask + (offset >> 1);

            // unrolled loop - handle case where loop starts on odd boundry
            if ((offset & 1) != 0)
            {
                *dest_ptr = m_filter.blend_pixel( *dest_ptr, *source++, *alpha_source++ >> 4 );
                dest_ptr++;
                count--;
            }
            // remember to handle the odd pixel at the end
            int remainder = (count & 1);
			int alpha;

            // handle 2 pixels at a time.
            count >>= 1;
            while (count--)
            {
                alpha = *alpha_source++;
                *dest_ptr = m_filter.blend_pixel( *dest_ptr, *source++, alpha & 0xF );
                dest_ptr++;
                *dest_ptr = m_filter.blend_pixel( *dest_ptr, *source++, alpha >> 4 );
                dest_ptr++;
            }
            // blend remaining pixel, if any
            if (remainder)
                *dest_ptr = m_filter.blend_pixel( *dest_ptr, *source, *alpha_source & 0xF );
		}
	};

}; // unnamed namespace

// --------------------------------------------------------
// drawing function template
// --------------------------------------------------------
template <class t_draw_function, typename t_source_type>
void draw_layer( t_screen_rect			source_rect, 
				 t_abstract_bitmap16&	dest,
                 t_screen_point			dest_point,
				 t_screen_rect const&	layer_rect, 
				 t_bitmap_row const*	rows, 
				 t_source_type const*	data,
				 t_draw_function		draw_func ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_source_type const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			draw_func.draw( count, dest_ptr, source, offset );
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}


t_bitmap_layer::~t_bitmap_layer()
{
}

// --------------------------------------------------------
// check if a layer contains a point
// --------------------------------------------------------
bool t_bitmap_layer::contains( t_screen_point point ) const
{
	if (!is_point_in_rect( point, m_rect ))
		return false;
	if (m_alpha_depth == 0)
		return true;

	point -= m_rect.top_left();

	t_bitmap_row const& row = m_rows[point.y];

    if (point.x < row.left || point.x >= row.right)
		return false;

	int index = row.offset + point.x - row.left;

	if (m_alpha_depth == 1)
		return data_contains( index );

	// 4 bit alpha
	int shift = (index & 1) << 2;

	return ((m_alpha_mask[index >> 1] >> shift) & 0xF) > 0;
}

// --------------------------------------------------------
// base class for bitmap layers
// get size of data (in pixels)
// --------------------------------------------------------
int t_bitmap_layer::get_data_size() const
{
	if (m_rows.get() == 0)
		return 0;

	t_bitmap_row const& last_row = m_rows[m_rect.height() - 1];

	return last_row.offset + last_row.right - last_row.left;
}

// --------------------------------------------------------
// base class for bitmap layers
// create alpha block information - summerizes alpha information for 32 pixel blocks
// --------------------------------------------------------
void t_bitmap_layer::set_block_alpha()
{
	t_uint8 const* source = m_alpha_mask.get();
	t_uint8*       dest;

	if (source == 0)
		return;

	int				data_size = get_data_size();
	int				block_size;
	int             block_shift = 0;
	int				alpha;
	int             alpha_shift;
	t_block_alpha	block_type;
	int				block_data_size = (data_size + (k_block_size << 2) + 3)
		                              >> (k_block_shift + 2);

	m_block_alpha = new t_uint8[block_data_size];
	dest = m_block_alpha.get();
	*dest = 0;
	while (data_size > 0)
	{
		block_size = k_block_size;
		if (block_size > data_size)
			block_size = data_size;
		data_size -= block_size;
		alpha = *source & 0xF;
		alpha_shift = 4;
		block_size--;
		if (alpha == 0)
			block_type = k_block_transparent;
		else if (alpha == 15)
			block_type = k_block_opaque;
		else
			block_type = k_block_four_bit_alpha;
		while (block_size > 0 && block_type != k_block_four_bit_alpha)
		{
			alpha = (*source >> alpha_shift) & 0xF;
			alpha_shift ^= 4;
			if (alpha_shift == 0)
				source++;
			if (alpha == 0)
			{
				if (block_type == k_block_opaque)
					block_type = k_block_one_bit_alpha;
			} else if (alpha == 15)
			{
				if (block_type == k_block_transparent)
					block_type = k_block_one_bit_alpha;
			}
			else
				block_type = k_block_four_bit_alpha;
			block_size--;
		}
		source += (block_size + 1) >> 1;
		*dest |= block_type << block_shift;
		block_shift += 2;
		if (block_shift == 8)
		{
			dest++;
			if (data_size > 0)
				*dest = 0;
			block_shift = 0;
		}
	}
}

// --------------------------------------------------------
// constructor
// --------------------------------------------------------
t_bitmap_layer_16::t_bitmap_layer_16( std::string const&        name, 
									  t_screen_rect const&      rect, 
									  int                       alpha_depth, 
		                              t_bitmap_row const* const rows, 
			                          t_shared_array<t_uint16>& data, 
									  t_shared_array<t_uint8>&  alpha_mask )
				 : m_data( data )
{
	m_name = name;
	m_rect = rect;
	m_alpha_depth = alpha_depth;
	m_alpha_mask = alpha_mask;

	// check if this is an empty image.
	if (m_rect.height() <= 0)
	{
		m_rows = 0;
		m_alpha_depth = 0;
		return;
	}

	m_rows = new t_bitmap_row[m_rect.height()];
	memcpy( m_rows.get(), rows, m_rect.height() * sizeof(t_bitmap_row));
	set_block_alpha();
}

// --------------------------------------------------------
// 16 bit, unpaletted bitmap
// create copy
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_16::adjust_brightness( int brightness ) const
{
	t_bitmap_layer_16* dest = new t_bitmap_layer_16( *this );

	if (brightness == 256)
		return dest;

	int             data_size = get_data_size();
	t_uint16*       dest_ptr;
	t_uint16 const* source_ptr;
	t_hue_pixel     pixel;

	dest->m_data = new t_uint16[data_size];
	dest_ptr = dest->m_data.get();
	source_ptr = m_data.get();
	while (data_size--)
	{
		pixel.set_color( *source_ptr );
		pixel.adjust_brightness( brightness );
		*dest_ptr = pixel.get_color();
		dest_ptr++;
		source_ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// 16 bit, unpaletted bitmap
// create copy
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_16::adjust_color( int target_hue, int hue_change, 
												 int saturation, int brightness ) const
{
	t_bitmap_layer_16* dest = new t_bitmap_layer_16( *this );

	int             data_size = get_data_size();
	t_uint16*       dest_ptr;
	t_uint16 const* source_ptr;
	t_hue_pixel     pixel;

	dest->m_data = new t_uint16[data_size];
	dest_ptr = dest->m_data.get();
	source_ptr = m_data.get();
	while (data_size--)
	{
		pixel.set_color( *source_ptr );
		pixel.adjust_hue( target_hue, hue_change );
		pixel.adjust_brightness( brightness );
		pixel.adjust_saturation( saturation );
		*dest_ptr = pixel.get_color();
		dest_ptr++;
		source_ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// 16 bit, unpaletted bitmap
// create copy
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_16::adjust_saturation( int delta ) const
{
	t_bitmap_layer_16* dest = new t_bitmap_layer_16( *this );

	if (delta == 256)
		return dest;

	int             data_size = get_data_size();
	t_uint16*       dest_ptr;
	t_uint16 const* source_ptr;
	t_hue_pixel     pixel;

	dest->m_data = new t_uint16[data_size];
	dest_ptr = dest->m_data.get();
	source_ptr = m_data.get();
	while (data_size--)
	{
		pixel.set_color( *source_ptr );
		pixel.adjust_saturation( delta );
		*dest_ptr = pixel.get_color();
		dest_ptr++;
		source_ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// 16 bit, unpaletted bitmap
// create copy
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_16::copy( t_screen_point const& origin,
		                                 t_bitmap_line_array const& shape ) const
{
	t_screen_rect			  rect;
	int					      alpha_depth;
	std::vector<t_bitmap_row> rows;
	std::vector<t_uint16>	  data;
	std::vector<t_uint8>	  alpha_mask;
	t_bitmap_row              new_row;
	t_bitmap_row const*       row_data;
	int						  y;
	int						  height;
	int						  bottom;
	int						  shape_index;
	int						  shape_height = shape.size();
	int						  data_size = 0;
	int						  copy_size;
	int						  left;
	int						  right;
	int						  offset;

	// compute row to start
	y = origin.y - m_rect.top;
	shape_index = 0;
	if (y < 0)
	{	// if row is off top, start on top row
		shape_index = -y;
		y = 0;
	}
	// compute height of copied area
	height = m_rect.height() - y;
	if (height + shape_index > shape_height)
		height = shape_height - shape_index;
	rect.top = y + m_rect.top;
	rect.bottom = rect.top;
	rect.left = m_rect.right;
	rect.right = m_rect.left;
	// reserve space for data
	data_size = get_data_size();
  rows.reserve( height >= 0 ? height : 0 );
	data.resize( data_size );
	alpha_mask.resize( (data_size + 1) >> 1 );
	// initialize pointer to row data
	row_data = m_rows.get() + y;
	alpha_depth = m_alpha_depth;
	data_size = 0;
	bottom = y + height;
	while (y < bottom)
	{	// compute left edge of row
		left = shape[shape_index].left + origin.x - m_rect.left;
		if (left < row_data->left)
			left = row_data->left;
		// compute right edge of row
		right = shape[shape_index++].right + origin.x - m_rect.left;
		if (right > row_data->right)
			right = row_data->right;
		// make sure right is not less than left
		if (right < left)
			right = left;
		// record start of data
		new_row.offset = data_size;
		if (left < right)
		{	// copy data
			offset = row_data->offset + left - row_data->left;
			copy_size = right - left;

			t_uint16 const* source = &m_data[offset];
			t_uint16*       dest   = &data[data_size];
			int				alpha_dest_offset   = data_size >> 1;
			int				alpha_dest_shift    = (data_size & 1) << 2;
			t_uint8*        dest_alpha   = &(*(alpha_mask.begin() + alpha_dest_offset));

			if (m_alpha_mask.get() == 0)
			{	
				// handle simple case, no alpha information
				memcpy( dest, source, copy_size << 1);
				data_size += copy_size;
				// set first nibble of alpha, if any
				if (alpha_dest_shift != 0)
				{
					*dest_alpha++ |= 0xF0;
					alpha_dest_shift = 0;
					copy_size--;
				}
				memset( dest_alpha, 0xFF, copy_size >> 1 );
				// set last nibble of alpha, if any
				if (copy_size & 1)
				{
					dest_alpha += copy_size >> 1;
					*dest_alpha = 0xF;
				}
			}
			else
			{ // copy  alpha information
				int				alpha_source_offset = offset >> 1;
				int				alpha_source_shift  = (offset & 1) << 2;
				int				alpha;
				t_uint8 const*	source_alpha = m_alpha_mask.get() + alpha_source_offset;
				bool			started = false;
				int				index = 0;
				int				start = left + 1;

				// copy pixel by pixel
				for (index = 0; index < copy_size; index++)
				{
					// get alpha mask for this pixel
					alpha = (*source_alpha >> alpha_source_shift) & 0xF;
					alpha_source_shift = 4 - alpha_source_shift;
					if (alpha_source_shift == 0)
						source_alpha++;
					// if alpha is non-zero, update right edge
					if (alpha != 0)
						right = index + start;
					// if alpha is zero, and this is the start, update left edge
					if (alpha == 0 && !started)
					{
						left++;
						source++;
					}
					else
					{
						// copy alpha information to destination
						started = true;
						if (alpha_dest_shift == 0)
						{
							*dest_alpha = alpha;
							alpha_dest_shift = 4;
						}
						else
						{
							*dest_alpha++ |= alpha << 4;
							alpha_dest_shift = 0;
						}
						// copy pixel
						*dest++ = *source++;
					}
				}
				// note how much we actually copied
				if (right < left)
					right = left;
				data_size += right - left;
			}
		}
		// save new row
		new_row.left = left;
		new_row.right = right;
		row_data++;
		y++;
		if (data_size > 0)
		{	// if we have any data, record this row
			rows.push_back( new_row );
			if (right > left)
				rect.bottom = rect.top + rows.size();
			// update the bounding rectangle
			if (left + m_rect.left < rect.left)
			{
				rect.left = left + m_rect.left;
				// if this is not the first row recorded, we have transparency
				if (rows.size() > 1)
					alpha_depth = 4;
			}
			// update the bounding rectangle
			if (right + m_rect.left > rect.right)
			{
				rect.right = right + m_rect.left;
				// if this is not the first row recorded, we have transparency
				if (rows.size() > 1)
					alpha_depth = 4;
			}
		}
		else
			rect.top++;
	}
	// make sure the rectangle makes sense
	if (rect.right < rect.left)
		rect.right = rect.left;
	if (rect.bottom < rect.top)
		rect.bottom = rect.top;

	// update the rows if the left edge of the rectangle is not the same as the source
	if (rect.left > m_rect.left)
	{
		int i;
		int difference = rect.left - m_rect.left;

		i = rows.size();
		while (i--)
		{
			rows[i].left -= difference;
			rows[i].right -= difference;
		}
	}

	// copy the data into shared arrays
	t_shared_array<t_uint16> data_ptr;
	t_shared_array<t_uint8>  alpha_mask_ptr;

	if (data_size > 0)
	{
		data_ptr = new t_uint16[data_size];
		memcpy( data_ptr.get(), &(*data.begin()), data_size << 1);
		if (alpha_depth > 0)
		{
			copy_size = (data_size + 1) >> 1;
			alpha_mask_ptr = new t_uint8[copy_size];
			memcpy( alpha_mask_ptr.get(), &(*alpha_mask.begin()), copy_size );
		}
	}

  if(rows.empty())
    return new t_bitmap_layer_16( m_name, rect, alpha_depth, NULL, data_ptr, alpha_mask_ptr );

  return new t_bitmap_layer_16( m_name, rect, alpha_depth, &(*rows.begin()), data_ptr, alpha_mask_ptr );
}

// --------------------------------------------------------
// data doesn't have any alpha information
// --------------------------------------------------------
bool t_bitmap_layer_16::data_contains( int index ) const
{
	return true;
}

// --------------------------------------------------------
// drawing function: 16 bit source, no alpha blending.
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
			      t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint16 const*		data ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                 offset;
    t_uint16 const*		source;
	t_uint16*           dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			memcpy( dest_ptr, source, count * sizeof( t_uint16 ));
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// draw paletted 16 bit source to destination, with alpha blending
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
				  t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint16 const*		data,
				  t_uint8 const*		alpha_mask,
				  t_uint8 const*        block_alpha )
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                 offset;
    t_uint16 const*		source;
	t_uint16*           dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;

			// find start
			t_uint8 const* alpha_source;
			int			   alpha;
			int            remainder;
			int            block_index      = offset >> k_block_shift;
			int            block_subindex   = (block_index & 3) << 1;
			t_uint8 const* block_alpha_ptr	= block_alpha + (block_index >> 2);
			int            block_size       = k_block_size - (offset & k_block_mask);

			while ( count > 0 )
			{
				if (block_size > count)
					block_size = count;
				count -= block_size;
				switch ((*block_alpha_ptr >> block_subindex) & 3)
				{
					case k_block_transparent:
						offset += block_size;
						source += block_size;
						dest_ptr += block_size;
						break;

					case k_block_opaque:
						offset += block_size;
						while (block_size--)
							*dest_ptr++ = *source++;
						break;

					case k_block_one_bit_alpha:
					case k_block_four_bit_alpha:
						 alpha_source = alpha_mask + (offset >> 1);
						// unrolled loop - handle case where loop starts on odd boundry
						if ((offset & 1) != 0)
						{
							*dest_ptr = alpha_blend16( *dest_ptr, *source++,
								                       *alpha_source++ >> 4 );
							dest_ptr++;
							offset++;
							block_size--;
						}
						// handle 2 pixels at a time.
						offset += block_size;
						remainder = block_size & 1;
						block_size >>= 1;
						while (block_size--)
						{
							alpha = *alpha_source++;
							*dest_ptr = alpha_blend16( *dest_ptr, *source++,
								                      alpha & 0xF );
							dest_ptr++;
							*dest_ptr = alpha_blend16( *dest_ptr, *source++, 
								                       alpha >> 4 );
							dest_ptr++;
						}
			            // blend remaining pixel, if any
			            if (remainder)
						{
							*dest_ptr = alpha_blend16( *dest_ptr, *source++,
								                       *alpha_source & 0xF );
							dest_ptr++;
						}
						break;
				}
				block_size = k_block_size;
				block_subindex += 2;
				if (block_subindex == 8)
				{
					block_alpha_ptr++;
					block_subindex = 0;
				}
			}
		}
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_bitmap_layer_16::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
							  t_screen_point dest_point ) const
{
	switch (m_alpha_depth)
	{
		case 0:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get() );
			break;
		}

		case 4:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    m_alpha_mask.get(), m_block_alpha.get() );
			break;
		}
	}
}

// --------------------------------------------------------
// drawing function: 16 bit source, fixed alpha blending.
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
			      t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint16 const*		data,
				  int                   alpha ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                 offset;
    t_uint16 const*		source;
	t_uint16*           dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			while (count--)
			{
				*dest_ptr = alpha_blend16( *dest_ptr, *source++, alpha );
				dest_ptr++;
			}
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// draw paletted 16 bit source to destination, with alpha blending
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
				  t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint16 const*		data,
				  t_uint8 const*		alpha_mask,
				  int                   image_alpha)
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                 offset;
    t_uint16 const*		source;
	t_uint16*           dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;

			// find start
			t_uint8 const* alpha_source = alpha_mask + (offset >> 1);

            // unrolled loop - handle case where loop starts on odd boundry
            if ((offset & 1) != 0)
            {
                *dest_ptr = alpha_blend16( *dest_ptr, *source++,
										   multiply_alpha16( image_alpha, *alpha_source++ >> 4 ));
                dest_ptr++;
                count--;
            }
            // remember to handle the odd pixel at the end
            int remainder = (count & 1);
			int alpha;

            // handle 2 pixels at a time.
            count >>= 1;
            while (count--)
            {
                alpha = *alpha_source++;
                *dest_ptr = alpha_blend16( *dest_ptr, *source++, 
					                       multiply_alpha16( image_alpha, alpha & 0xF ) );
                dest_ptr++;
                *dest_ptr = alpha_blend16( *dest_ptr, *source++, 
					                       multiply_alpha16( image_alpha, alpha >> 4 ) );
                dest_ptr++;
            }
            // blend remaining pixel, if any
            if (remainder)
                *dest_ptr = alpha_blend16( *dest_ptr, *source, 
				                           multiply_alpha16( image_alpha, *alpha_source & 0xF) );
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_bitmap_layer_16::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
							  t_screen_point dest_point, int image_alpha ) const
{
	switch (m_alpha_depth)
	{
		case 0:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(), 
				    image_alpha );
			break;
		}

		case 4:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    m_alpha_mask.get(), image_alpha );
			break;
		}
	}
}

// --------------------------------------------------------
// check if data is non-zero
// --------------------------------------------------------
bool t_paletted_layer::data_contains( int index ) const
{
	return m_data[index] != 0;
}

// --------------------------------------------------------
// body of bitmap_layer.cpp
// --------------------------------------------------------
t_paletted_16_layer::t_paletted_16_layer( t_bitmap_layer_24 const& source )
              : t_paletted_layer( source )
{
	std::vector<t_pixel_24> const& palette = source.get_palette();

	std::vector<t_pixel_24>::const_iterator index;

	m_palette.reserve( palette.size() );
	for (index = palette.begin(); index != palette.end(); index++)
		m_palette.push_back( convert_to_16_bit( *index ) );
}

// --------------------------------------------------------
// body of bitmap_layer.cpp
// --------------------------------------------------------
t_paletted_16_layer::t_paletted_16_layer( std::string const&			name,
										  t_screen_rect const&			rect, 
				 		                  int							alpha_depth, 
										  std::vector<t_uint16> const&	palette, 
		                                  t_bitmap_row const* const		rows, 
			                              t_shared_array<t_uint8>&		data, 
										  t_shared_array<t_uint8>&		alpha_mask )
{
	m_name = name;
	m_rect = rect;
	m_alpha_depth = alpha_depth;
	m_palette = palette;
	m_data = data;
	m_alpha_mask = alpha_mask;

	// check if this is an empty image.
	if (m_rect.height() <= 0)
	{
		m_rows = 0;
		m_alpha_depth = 0;
		return;
	}

	m_rows = new t_bitmap_row[m_rect.height()];
	memcpy( m_rows.get(), rows, m_rect.height() * sizeof(t_bitmap_row));
	set_block_alpha();
}

// --------------------------------------------------------
// paletted 16 bit layer
// --------------------------------------------------------
t_bitmap_layer* t_paletted_16_layer::adjust_brightness( int brightness ) const
{
	t_paletted_16_layer* dest;

	dest = new t_paletted_16_layer( *this );
	if (brightness == 256)
		return dest;

	// shallow copy of data, deep copy of palette.
	t_uint16*   ptr = &(*dest->m_palette.begin());
	int         count = dest->m_palette.size();
	t_hue_pixel pixel;

	while (count--)
	{
		pixel.set_color( *ptr );
		pixel.adjust_brightness( brightness );
		*ptr = pixel.get_color();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// paletted 16 bit layer
// --------------------------------------------------------
t_bitmap_layer* t_paletted_16_layer::adjust_color( int target_hue, int hue_change, 
												   int saturation, int brightness ) const
{
	t_paletted_16_layer* dest;

	dest = new t_paletted_16_layer( *this );

	// shallow copy of data, deep copy of palette.
	t_uint16*   ptr = &(*dest->m_palette.begin());
	int         count = dest->m_palette.size();
	t_hue_pixel pixel;

	while (count--)
	{
		pixel.set_color( *ptr );
		pixel.adjust_brightness( brightness );
		pixel.adjust_hue( target_hue, hue_change );
		pixel.adjust_saturation( saturation );
		*ptr = pixel.get_color();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// paletted 16 bit layer
// --------------------------------------------------------
t_bitmap_layer* t_paletted_16_layer::adjust_saturation( int saturation ) const
{
	t_paletted_16_layer* dest;

	dest = new t_paletted_16_layer( *this );
	if (saturation == 256)
		return dest;

	// shallow copy of data, deep copy of palette.
	t_uint16*   ptr = &(*dest->m_palette.begin());
	int         count = dest->m_palette.size();
	t_hue_pixel pixel;

	while (count--)
	{
		pixel.set_color( *ptr );
		pixel.adjust_saturation( saturation );
		*ptr = pixel.get_color();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// paletted 16 bit layer
// create copy
// --------------------------------------------------------
t_bitmap_layer* t_paletted_16_layer::copy( t_screen_point const& origin,
		                                 t_bitmap_line_array const& shape ) const
{
	t_screen_rect			  rect;
	int					      alpha_depth;
	std::vector<t_bitmap_row> rows;
	std::vector<t_uint8>	  data;
	std::vector<t_uint8>	  alpha_mask;
	t_bitmap_row              new_row;
	t_bitmap_row const*       row_data;
	int						  y;
	int						  height;
	int						  bottom;
	int						  shape_index;
	int						  shape_height = shape.size();
	int						  data_size = 0;
	int						  copy_size;
	int						  left;
	int						  right;
	int						  offset;

	// compute row to start
	y = origin.y - m_rect.top;
	shape_index = 0;
	if (y < 0)
	{	// if row is off top, start on top row
		shape_index = -y;
		y = 0;
	}
	// compute height of copied area
	height = m_rect.height() - y;
	if (height + shape_index > shape_height)
		height = shape_height - shape_index;
	rect.top = y + m_rect.top;
	rect.bottom = rect.top;
	rect.left = m_rect.right;
	rect.right = m_rect.left;
	// reserve space for data
	data_size = get_data_size();
	rows.reserve( height );
	data.resize( data_size );
	alpha_mask.resize( (data_size + 1) >> 1 );
	// initialize pointer to row data
	row_data = m_rows.get() + y;
	alpha_depth = m_alpha_depth;
	data_size = 0;
	bottom = y + height;
	while (y < bottom)
	{	// compute left edge of row
		left = shape[shape_index].left + origin.x - m_rect.left;
		if (left < row_data->left)
			left = row_data->left;
		// compute right edge of row
		right = shape[shape_index++].right + origin.x - m_rect.left;
		if (right > row_data->right)
			right = row_data->right;
		// make sure right is not less than left
		if (right < left)
			right = left;
		// record start of data
		new_row.offset = data_size;
		if (left < right)
		{	// copy data
			offset = row_data->offset + left - row_data->left;
			copy_size = right - left;

			t_uint8 const*  source = &m_data[offset];
			t_uint8*        dest   = &data[data_size];
			int				alpha_dest_offset   = data_size >> 1;
			int				alpha_dest_shift    = (data_size & 1) << 2;
			t_uint8*        dest_alpha   = &(*(alpha_mask.begin() + alpha_dest_offset));

			if (m_alpha_mask.get() == 0)
			{	// handle simple case, no alpha information
				if (alpha_depth > 0)
				{
					while (left < right && *source == 0)
					{
						left++;
						source++;
					}
					while ( left < right && source[right - left - 1] == 0)
						right--;
					copy_size = right - left;
				}
				memcpy( dest, source, copy_size );
				data_size += copy_size;
			}
			else
			{ // copy  alpha information
				int				alpha_source_offset = offset >> 1;
				int				alpha_source_shift  = (offset & 1) << 2;
				int				alpha;
				t_uint8 const*	source_alpha = m_alpha_mask.get() + alpha_source_offset;
				bool			started = false;
				int				index = 0;
				int				start = left + 1;

				// copy pixel by pixel
				for (index = 0; index < copy_size; index++)
				{
					// get alpha mask for this pixel
					alpha = (*source_alpha >> alpha_source_shift) & 0xF;
					alpha_source_shift = 4 - alpha_source_shift;
					if (alpha_source_shift == 0)
						source_alpha++;
					// if alpha is non-zero, update right edge
					if (alpha != 0)
						right = index + start;
					// if alpha is zero, and this is the start, update left edge
					if (alpha == 0 && !started)
					{
						left++;
						source++;
					}
					else
					{
						// copy alpha information to destination
						started = true;
						if (alpha_dest_shift == 0)
						{
							*dest_alpha = alpha;
							alpha_dest_shift = 4;
						}
						else
						{
							*dest_alpha++ |= alpha << 4;
							alpha_dest_shift = 0;
						}
						// copy pixel
						*dest++ = *source++;
					}
				}
				// note how much we actually copied
				if (right < left)
					right = left;
				data_size += right - left;
			}
		}
		// save new row
		new_row.left = left;
		new_row.right = right;
		row_data++;
		y++;
		if (data_size > 0)
		{	// if we have any data, record this row
			rows.push_back( new_row );
			if (right > left)
				rect.bottom = rect.top + rows.size();
			// update the bounding rectangle
			if (left + m_rect.left < rect.left)
			{
				rect.left = left + m_rect.left;
				// if this is not the first row recorded, we have transparency
				if (alpha_depth == 0 && rows.size() > 1)
					alpha_depth = 1;
			}
			// update the bounding rectangle
			if (right + m_rect.left > rect.right)
			{
				rect.right = right + m_rect.left;
				// if this is not the first row recorded, we have transparency
				if (alpha_depth == 0 && rows.size() > 1)
					alpha_depth = 1;
			}
		}
		else
			rect.top++;
	}
	// make sure the rectangle makes sense
	if (rect.right < rect.left)
		rect.right = rect.left;
	if (rect.bottom < rect.top)
		rect.bottom = rect.top;

	// update the rows if the left edge of the rectangle is not the same as the source
	if (rect.left > m_rect.left)
	{
		int i;
		int difference = rect.left - m_rect.left;

		i = rows.size();
		while (i--)
		{
			rows[i].left -= difference;
			rows[i].right -= difference;
		}
	}

	// copy the data into shared arrays
	t_shared_array<t_uint8> data_ptr;
	t_shared_array<t_uint8>  alpha_mask_ptr;

	if (data_size > 0)
	{
		data_ptr = new t_uint8[data_size];
		memcpy( data_ptr.get(), &(*data.begin()), data_size);
		if (alpha_depth > 0)
		{
			copy_size = (data_size + 1) >> 1;
			alpha_mask_ptr = new t_uint8[copy_size];
			memcpy( alpha_mask_ptr.get(), &(*alpha_mask.begin()), copy_size );
		}
	}
	return new t_paletted_16_layer( m_name, rect, alpha_depth, m_palette,
		                            &(*rows.begin()), data_ptr, alpha_mask_ptr );

}


// --------------------------------------------------------
// drawing function: paletted 16 bit source, no alpha blending.
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
			      t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint8 const*		data,
		  		  t_uint16 const*		palette ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_uint8 const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			while (count--)
				*dest_ptr++ = palette[*source++];
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function: paletted 16 bit source, no alpha blending.
// --------------------------------------------------------
static void draw_transparent( t_screen_rect			source_rect, 
							  t_abstract_bitmap16&	dest,
							  t_screen_point		dest_point,
							  t_screen_rect const&	layer_rect, 
							  t_bitmap_row const*	rows, 
							  t_uint8 const*		data,
		  					  t_uint16 const*		palette ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_uint8 const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			while (count--)
			{
				if (*source != 0)
					*dest_ptr = palette[*source];
				source++;
				dest_ptr++;
			}
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// draw paletted 16 bit source to destination, with alpha blending
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
				  t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint8 const*		data,
				  t_uint16 const*		palette,
				  t_uint8 const*		alpha_mask,
				  t_uint8 const*        block_alpha )
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top ) + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                 offset;
    t_uint8 const*		source;
	t_uint16*           dest_ptr;

	static t_uint32			alpha_8;
	static t_uint32			source_4a;
	static t_uint32			source_4b;
	static t_uint32			dest_pair;
	
	static t_uint32*		alpha_8_ptr;
	static t_uint32*		source_4_ptr;
	static t_uint32*		dest_2_ptr;	
	static t_uint32			pixel_pair;	
	
    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;

			// find start
			t_uint8 const* alpha_source;
			int			   alpha=0;
			int            remainder;
			int            block_index      = offset >> k_block_shift;
			int            block_subindex   = (block_index & 3) << 1;
			t_uint8 const* block_alpha_ptr	= block_alpha + (block_index >> 2);
			int            block_size       = k_block_size - (offset & k_block_mask);

			while ( count > 0 )
			{
				if (block_size > count)
					block_size = count;
				count -= block_size;
				switch ((*block_alpha_ptr >> block_subindex) & 3)
				{
					case k_block_transparent:
						offset += block_size;
						source += block_size;
						dest_ptr += block_size;
						break;

					case k_block_opaque:
						offset += block_size;

						while (block_size--)
							*dest_ptr++ = palette[ *source++ ];
						break;

					case k_block_one_bit_alpha:
						offset += block_size;
						while (block_size--)
						{
							if (*source != 0)
								*dest_ptr = palette[ *source ];
							source++;
							dest_ptr++;
						}
						break;

					case k_block_four_bit_alpha:
						alpha_source = alpha_mask + (offset >> 1);
						// unrolled loop - handle case where loop starts on odd boundry
						if ((offset & 1) != 0)
						{
							*dest_ptr = alpha_blend16( *dest_ptr, palette[ *source++ ],
								                      *alpha_source++ >> 4 );
							dest_ptr++;
							offset++;
							block_size--;
						}
						// handle 2 pixels at a time.
						offset		+= block_size;
						remainder	=  block_size & 1;
						block_size >>= 1;
						
						alpha_8_ptr		= (t_uint32*)alpha_source;
						source_4_ptr	= (t_uint32*)source;
						dest_2_ptr		= (t_uint32*)dest_ptr;
						pixel_pair		= 0;
						
						int original_block_size = block_size;
						
						while (block_size)
						{
							if (block_size < 4)
							{
								alpha_8 = 0;
								memcpy( &alpha_8, alpha_8_ptr, block_size );
								if (block_size <= 2)
								{
									source_4a = 0;
									memcpy( &source_4a, source_4_ptr, block_size << 1 );
								}
								else
								{
									source_4a = *source_4_ptr++;
									source_4b = *(t_uint16*)(source_4_ptr);		
								}
							}
							else
							{
								alpha_8			= *alpha_8_ptr;
								alpha_8_ptr++;

								source_4a		= *source_4_ptr;
								source_4_ptr++;
								
								source_4b		= *source_4_ptr;
								source_4_ptr++;
							}
							
							//do 32 bytes of pixels at a time
							
							//pixels are identified by their number (1-8)
							
							//source is mapped from 8 -> 32 
							///////////////////////////////////////////
							//	4 |  3  | 2  | 1 | 8 |  7  | 6  | 5 
							///////////////////////////////////////////
							
							//dest is mapped from 16 -> 32 
							///////////////////////////////////////////
							//	2 |  1  | 4  | 3 | 6 |  5  | 8  | 7
							///////////////////////////////////////////
							
							//alpha is mapped from 4 -> 32 
							///////////////////////////////////////////
							// 8  | 7 | 6 | 5 | 4 | 3 | 2 | 1 
							///////////////////////////////////////////
					
							/////////////////////////////////////////////////////////////////
							
							dest_pair		= *dest_2_ptr;

							pixel_pair  = alpha_blend16( dest_pair & 0x0000FFFF, 
														 palette[ source_4a & 0xFF ],	
													     alpha_8 & 0x0000000F );
							
							pixel_pair |= alpha_blend16( dest_pair >> 16, 
													     palette[ (source_4a >> 8) & 0xFF ],	
														 (alpha_8 >> 4) & 0xF ) << 16;
							
							*dest_2_ptr = pixel_pair;
							
							/////////////////////////////////////////////////////////////////
							
							block_size--;
							if (block_size <= 0) 
								break;
								
							dest_2_ptr++;
						
							/////////////////////////////////////////////////////////////////
							
							dest_pair		= *dest_2_ptr;

							pixel_pair  = alpha_blend16( dest_pair & 0x0000FFFF,
														 palette[ (source_4a >> 16) & 0xFF ],	
														 (alpha_8 >> 8) & 0xF );
							
							pixel_pair |= alpha_blend16( dest_pair >> 16, 
														 palette[ (source_4a >> 24) & 0xFF ],	
														 (alpha_8 >> 12) & 0xF ) << 16;
							
							*dest_2_ptr = pixel_pair;

							/////////////////////////////////////////////////////////////////
							
							block_size--;
							if (block_size <= 0) 
								break;
													
							dest_2_ptr++;
							
							/////////////////////////////////////////////////////////////////
							
							dest_pair		= *dest_2_ptr;

							pixel_pair  = alpha_blend16( dest_pair & 0x0000FFFF, 
														 palette[ (source_4b & 0x000000FF) ],
														 (alpha_8 >> 16) & 0xF );
							
							pixel_pair |= alpha_blend16( dest_pair >> 16, 
														 palette[ (source_4b >> 8) & 0xFF ],
														 (alpha_8 >> 20) & 0xF ) <<16;
							
							*dest_2_ptr = pixel_pair;
							
							/////////////////////////////////////////////////////////////////
							
							block_size--;
							if (block_size <= 0) 
								break;
								
							dest_2_ptr++;
							
							/////////////////////////////////////////////////////////////////
							
							dest_pair		= *dest_2_ptr;

							pixel_pair  = alpha_blend16( (dest_pair & 0x0000FFFF), 
														 palette[ (source_4b >> 16) & 0xFF ],	
														 (alpha_8 >> 24) & 0xF );
							
							pixel_pair |= alpha_blend16( dest_pair >> 16,
														 palette[ source_4b >> 24 ],
														 alpha_8 >> 28 ) << 16;
							
							*dest_2_ptr = pixel_pair;
			
							/////////////////////////////////////////////////////////////////
							
							block_size--;
							if (block_size <= 0) 
								break;			
							
							dest_2_ptr++;
						}
						
						int delta = original_block_size;
						
						alpha_source		+= delta;
						dest_ptr			+= 2*delta;
						source				+= 2*delta;
						
						// blend remaining pixel, if any
						if (remainder)
						{
							*dest_ptr = alpha_blend16( *dest_ptr, palette[ *source++ ],
								                       *alpha_source & 0xF );
							dest_ptr++;
						}
						break;
				}

				block_size = k_block_size;
				block_subindex += 2;
				if (block_subindex == 8)
				{
					block_alpha_ptr++;
					block_subindex = 0;
				}
			}
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_paletted_16_layer::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
                                t_screen_point dest_point ) const
{
	switch (m_alpha_depth)
	{
		case 0:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    &(*m_palette.begin()) );
			break;
		}

		case 1:
		{
			::draw_transparent( source_rect, dest, dest_point, m_rect, m_rows.get(), 
				                m_data.get(), &(*m_palette.begin()) );
			break;
		}

		case 4:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    &(*m_palette.begin()), m_alpha_mask.get(), m_block_alpha.get() );
			break;
		}
	}
}

// --------------------------------------------------------
// drawing function: paletted 16 bit source, fixed alpha blending.
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
			      t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint8 const*		data,
		  		  t_uint16 const*		palette,
				  int                   alpha ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_uint8 const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			while (count--)
				*dest_ptr++ = alpha_blend16( *dest_ptr, palette[*source++], alpha );
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function: paletted 16 bit source, no alpha blending.
// --------------------------------------------------------
static void draw_transparent( t_screen_rect			source_rect, 
							  t_abstract_bitmap16&	dest,
							  t_screen_point		dest_point,
							  t_screen_rect const&	layer_rect, 
							  t_bitmap_row const*	rows, 
							  t_uint8 const*		data,
		  					  t_uint16 const*		palette,
							  int                   alpha ) 
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_uint8 const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count  = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;
			while (count--)
			{
				if (*source != 0)
					*dest_ptr = alpha_blend16( *dest_ptr, palette[*source], alpha );
				source++;
				dest_ptr++;
			}
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// draw paletted 16 bit source to destination, with alpha blending
// --------------------------------------------------------
static void draw( t_screen_rect			source_rect, 
				  t_abstract_bitmap16&	dest,
				  t_screen_point		dest_point,
				  t_screen_rect const&	layer_rect, 
				  t_bitmap_row const*	rows, 
				  t_uint8 const*		data,
				  t_uint16 const*		palette,
				  t_uint8 const*		alpha_mask,
				  int                   image_alpha )
{
    t_screen_point dest_offset = dest_point - source_rect.top_left();
    t_screen_rect  dest_rect;

    // find clipping rectangle
    dest_rect = intersection( intersection( source_rect, layer_rect ) + dest_offset,
                              dest.get_rect() );
    if (dest_rect.width() <= 0 || dest_rect.height() <= 0)
        return;

    // find source rectangle, with origin moved to (0,0)
    source_rect = dest_rect - dest_offset - layer_rect.top_left();

    int            width = dest_rect.width();
    int            height = dest_rect.height();
    t_uint16*      row_start = dest.advance_line( dest.get_data_ptr(), dest_rect.top )
                               + dest_rect.left;
    t_bitmap_row const* source_row = rows + source_rect.top;
    int                 left;
    int                 right;
    int                 count;
	int                  offset;
    t_uint8 const* source;
	t_uint16*            dest_ptr;

    while (height--)
    {
        // find segment of this row to be drawn
        if (source_rect.left < source_row->left)
            left = source_row->left;
        else
            left = source_rect.left;
        if (source_rect.right > source_row->right)
            right = source_row->right;
        else
            right = source_rect.right;
        if (left < right)
        {            
            // compute start of row in source bitmap
            offset = source_row->offset + left - source_row->left;
            source = data + offset;
            count = right - left;

            // move pointer for destination
            dest_ptr = row_start + left - source_rect.left;

			// find start
			t_uint8 const* alpha_source = alpha_mask + (offset >> 1);

            // unrolled loop - handle case where loop starts on odd boundry
            if ((offset & 1) != 0)
            {
                *dest_ptr = alpha_blend16( *dest_ptr, palette[ *source++ ], 
										   multiply_alpha16( image_alpha, *alpha_source++ >> 4 ));
                dest_ptr++;
                count--;
            }
            // remember to handle the odd pixel at the end
            int remainder = (count & 1);
			int alpha;

            // handle 2 pixels at a time.
            count >>= 1;
            while (count--)
            {
                alpha = *alpha_source++;
                *dest_ptr = alpha_blend16( *dest_ptr, palette[ *source++ ], 
										   multiply_alpha16( image_alpha, alpha & 0xF) );
                dest_ptr++;
                *dest_ptr = alpha_blend16( *dest_ptr, palette[ *source++ ], 
										   multiply_alpha16( image_alpha, alpha >> 4 ));
                dest_ptr++;
            }
            // blend remaining pixel, if any
            if (remainder)
                *dest_ptr = alpha_blend16( *dest_ptr, palette[ *source ], 
										   multiply_alpha16( image_alpha, *alpha_source & 0xF) );
        }
        source_row++;
        row_start = dest.advance_line( row_start );
    }   
}

// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_paletted_16_layer::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
                                t_screen_point dest_point, int image_alpha ) const
{
	switch (m_alpha_depth)
	{
		case 0:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    &(*m_palette.begin()), image_alpha );
			break;
		}

		case 1:
		{
			::draw_transparent( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				                &(*m_palette.begin()), image_alpha );
			break;
		}

		case 4:
		{
			::draw( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				    &(*m_palette.begin()), m_alpha_mask.get(), image_alpha );
			break;
		}
	}
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// --------------------------------------------------------
void t_bitmap_layer_24::init( std::string const& name, t_screen_rect const& rect, int alpha_depth, 
		                      std::vector<t_pixel_24> const& palette, 
							  t_bitmap_row const* const rows,  t_uint8 const* const data, 
							  t_uint8 const* const alpha_mask )
{
	m_name = name;
	m_rect = rect;
	m_alpha_depth = alpha_depth;
	m_palette = palette;
	// check if this is an empty image.
	if (m_rect.height() <= 0)
	{
		m_rows = 0;
		m_data = 0;
		m_alpha_mask = 0;
		m_alpha_depth = 0;
		return;
	}

	m_rows = new t_bitmap_row[m_rect.height()];
	memcpy( m_rows.get(), rows, m_rect.height() * sizeof(t_bitmap_row));

	t_bitmap_row& last_row = m_rows[ m_rect.height() - 1 ];
	int           data_size = last_row.get_width() + last_row.offset;

	m_data = new t_uint8[data_size];
	memcpy( m_data.get(), data, data_size );

	if (alpha_depth < 4)
		m_alpha_mask = 0;
	else
	{
		data_size = (data_size + 1) >> 1;
		m_alpha_mask = new t_uint8[data_size];
		memcpy( m_alpha_mask.get(), alpha_mask, data_size );
		set_block_alpha();
	}
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// --------------------------------------------------------
void t_bitmap_layer_24::init( std::string const& name, t_screen_rect const& rect, int alpha_depth, 
		                      std::vector<t_pixel_24> const& palette, 
							  t_bitmap_row const* const rows,  t_shared_array<t_uint8>& data, 
							  t_shared_array<t_uint8>& alpha_mask )
{
	m_name = name;
	m_rect = rect;
	m_alpha_depth = alpha_depth;
	m_palette = palette;
	m_data = data;
	m_alpha_mask = alpha_mask;

	// check if this is an empty image.
	if (m_rect.height() <= 0)
	{
		m_rows = 0;
		m_alpha_depth = 0;
		return;
	}

	m_rows = new t_bitmap_row[m_rect.height()];
	memcpy( m_rows.get(), rows, m_rect.height() * sizeof(t_bitmap_row));
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// copy with brightness
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_24::adjust_brightness( int brightness ) const
{
	t_bitmap_layer_24* dest;

	dest = new t_bitmap_layer_24( *this );
	if (brightness == 256)
		return dest;

	// shallow copy of data, deep copy of palette.
	t_pixel_24*   ptr = &(*dest->m_palette.begin());
	int           count = dest->m_palette.size();
	t_hue_pixel   pixel;

	while (count--)
	{
		pixel.set_color( ptr->red, ptr->green, ptr->blue );
		pixel.adjust_brightness( brightness );
		*ptr = pixel.get_pixel_24();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// copy with brightness
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_24::adjust_color( int target_hue, int hue_change, 
												 int saturation,
		                                         int brightness ) const
{
	t_bitmap_layer_24* dest;

	dest = new t_bitmap_layer_24( *this );

	// shallow copy of data, deep copy of palette.
	t_pixel_24*   ptr = &(*dest->m_palette.begin());
	int           count = dest->m_palette.size();
	t_hue_pixel   pixel;

	while (count--)
	{
		pixel.set_color( ptr->red, ptr->green, ptr->blue );
		pixel.adjust_hue( target_hue, hue_change );
		pixel.adjust_brightness( brightness );
		pixel.adjust_saturation( saturation );
		*ptr = pixel.get_pixel_24();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// copy with brightness
// --------------------------------------------------------
t_bitmap_layer* t_bitmap_layer_24::adjust_saturation( int saturation ) const
{
	t_bitmap_layer_24* dest;

	dest = new t_bitmap_layer_24( *this );
	if (saturation == 256)
		return dest;

	// shallow copy of data, deep copy of palette.
	t_pixel_24*   ptr = &(*dest->m_palette.begin());
	int           count = dest->m_palette.size();
	t_hue_pixel   pixel;

	while (count--)
	{
		pixel.set_color( ptr->red, ptr->green, ptr->blue );
		pixel.adjust_saturation( saturation );
		*ptr = pixel.get_pixel_24();
		ptr++;
	}
	return dest;
}

// --------------------------------------------------------
// read and update progress handler
// --------------------------------------------------------
int read( std::streambuf& buffer, void* data, t_uint32 size, t_progress_handler* handler )
{
	if (handler == 0)
		return read( buffer, data, size );

	char*	ptr = reinterpret_cast<char*>(data);
	int     bytes_to_read;
	int		bytes_read;
	int     result = 0;
	
	while (size > 0)
	{
		bytes_to_read = size;
		if (bytes_to_read > 65536)
			bytes_to_read = 65536;
		bytes_read = read( buffer, ptr, bytes_to_read );
		if (bytes_read < bytes_to_read)
			size = 0;
		else
			size -= bytes_read;
		handler->increment( bytes_read );
		ptr += bytes_read;
		result += bytes_read;
	}
	return result;
}


// --------------------------------------------------------
// 24 bit paletted bitmap layer
// --------------------------------------------------------
bool t_bitmap_layer_24::read( std::streambuf& buffer, t_progress_handler* handler )
{
	int palette_size = get<t_uint16>( buffer );
	int version		 = get<t_uint16>( buffer );

    m_palette.resize( palette_size );
	::read( buffer, &(*m_palette.begin()), m_palette.size() * sizeof(t_pixel_24), handler );

    if (!read_string16( buffer, m_name ))
		return false;
	m_alpha_depth = buffer.sbumpc();
	::read( buffer, &m_rect, sizeof(m_rect) );

	// compute how much we've read so far
	if (handler != 0)
		handler->increment( 7 + sizeof( m_rect ) + m_name.length() );            

	// check if the image is empty
	if (m_rect.height() <= 0)
	{
		m_rows = 0;
		m_data = 0;
		return true;
	}

	m_rows = new t_bitmap_row[m_rect.height()];
	::read( buffer, m_rows.get(), m_rect.height() * sizeof(t_bitmap_row), handler );

	t_bitmap_row& last_row = m_rows[ m_rect.height() - 1 ];
	int           data_size = last_row.get_width() + last_row.offset;

	m_data = new t_uint8[data_size];
	::read( buffer, m_data.get(), data_size, handler );

	if (m_alpha_depth < 4)
		m_alpha_mask = 0;
	else
	{
		int alpha_size = (data_size + 1) >> 1;
		m_alpha_mask = new t_uint8[alpha_size];
		::read( buffer, m_alpha_mask.get(), alpha_size, handler );
		if (version == 0)
			set_block_alpha();
		else
		{
			int	block_data_size = (data_size + (k_block_size << 2) + 3)
		                           >> (k_block_shift + 2);
			m_block_alpha = new t_uint8[block_data_size];
			::read( buffer, m_block_alpha.get(), block_data_size, handler );
		}
	}
	return true;
}

// --------------------------------------------------------
// 24 bit paletted bitmap layer
// --------------------------------------------------------
bool t_bitmap_layer_24::write( std::streambuf& buffer ) const
{
    put<t_uint16>( buffer, m_palette.size() );
	put<t_uint16>( buffer, 1 ); // version
	::write( buffer, &(*m_palette.begin()), m_palette.size() * sizeof(t_pixel_24) );

    if (!write_string16( buffer, m_name ))
		return false;
	buffer.sputc( m_alpha_depth );
	::write( buffer, &m_rect, sizeof(m_rect) );

	// don't write anything else if the image is empty
	if (m_rect.height() <= 0)
		return true;

	::write( buffer, m_rows.get(), m_rect.height() * sizeof(t_bitmap_row) );

	t_bitmap_row& last_row = m_rows[ m_rect.height() - 1 ];
	int           data_size = last_row.get_width() + last_row.offset;

	::write( buffer, m_data.get(), data_size );

	if (m_alpha_depth == 4)
	{
		int alpha_data_size = (data_size + 1) >> 1;
		int	block_data_size = (data_size + (k_block_size << 2) + 3)
		                      >> (k_block_shift + 2);

		::write( buffer, m_alpha_mask.get(), alpha_data_size );
		::write( buffer, m_block_alpha.get(), block_data_size );
	}
	return true;
}


// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_bitmap_layer_24::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
                              t_screen_point dest_point ) const
{
	t_paletted_24_source source( &(*m_palette.begin()) );

	switch (m_alpha_depth)
	{
		case 0:
		{
			t_simple_draw<t_paletted_24_source> draw_func( source );

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}

		case 1:
		{
			t_draw_alpha_1<t_paletted_24_source> draw_func( source );

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}

		case 4:
		{
			t_draw_alpha_4<t_paletted_24_source> draw_func( source, 
				                                                     m_alpha_mask.get() );

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}
	}
}

// --------------------------------------------------------
// drawing function
// --------------------------------------------------------
void t_bitmap_layer_24::draw( t_screen_rect source_rect, t_abstract_bitmap16& dest,
                              t_screen_point dest_point, int alpha ) const
{
	t_blended_paletted_24_source source( &(*m_palette.begin()), alpha );

	switch (m_alpha_depth)
	{
		case 0:
		{
			t_simple_draw<t_blended_paletted_24_source> draw_func( source );

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}

		case 1:
		{
			t_draw_alpha_1<t_blended_paletted_24_source> draw_func( source );

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}

		case 4:
		{
			t_draw_alpha_4<t_blended_paletted_24_source> draw_func( source,
				                                                             m_alpha_mask.get());

			draw_layer( source_rect, dest, dest_point, m_rect, m_rows.get(), m_data.get(),
				        draw_func );
			break;
		}
	}
}

// copy not yet implemented.
t_bitmap_layer* t_bitmap_layer_24::copy( t_screen_point const& origin,
		                                 t_bitmap_line_array const& shape ) const
{
	return 0;
}

